package org.domain.acount.session;

import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
import javax.faces.component.UISelectItems;
import javax.faces.component.html.HtmlCommandButton;
import javax.faces.component.html.HtmlForm;
import javax.faces.component.html.HtmlInputHidden;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.component.html.HtmlPanelGrid;
import javax.faces.component.html.HtmlSelectOneRadio;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.xml.JRXmlLoader;

import org.domain.acount.util.ReportConfiguration;
import org.domain.acount.util.ReportItem;
import org.gvlabs.jsf.component.report.JSFComponentMapper;
import org.gvlabs.jsf.component.report.RFComponentMapper;
import org.gvlabs.util.report.servlet.ReportServlet;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.richfaces.component.html.HtmlCalendar;
import org.richfaces.component.html.HtmlInputNumberSpinner;
import org.richfaces.component.state.TreeState;
import org.richfaces.event.NodeSelectedEvent;
import org.richfaces.event.UploadEvent;
import org.richfaces.model.UploadItem;

/**
 * Report Component Bean
 *
 * @author Thiago Galbiatti Vespa (thiago@thiagovespa.com.br)
 * @version 1.0
 *
 */
@Name("reportTree")
@Scope(ScopeType.SESSION)
public class ReportTree implements Serializable {

	/**
	 *
	 */
	private static final long serialVersionUID = 201002060938L;

	private ReportItem selectedNode;

	private String path = ReportConfiguration.getString("reportPath"); //$NON-NLS-1$

	private String folderName;

	private Set<ReportItem> tree = null;

	private TreeState treeState;

	private String msg;

	/**
	 * Report max count token
	 */
	public final static String REPORT_MAX_COUNT_TOKEN = "REPORT_MAX_COUNT";
	/**
	 * is ignore pagination token
	 */
	public final static String IS_IGNORE_PAGINATION_TOKEN = "IS_IGNORE_PAGINATION";

	/**
	 * Report path param
	 */
	public final static String REPORT_PATH_PARAM = "reportPath";
	/**
	 * Generate button param
	 */
	private static final String GENERATE_BTN_PARAM = "generateBtn";

	// Workaround - Colocar em um component
	private HtmlForm reportCmp;

	private static final ReportItem EMPTY_REPORT = new ReportItem("Raiz", "",
			false);

	private static final Locale BRAZIL_LOC = new Locale("pt", "BR");

	/**
	 * Default constructor.
	 */
	public ReportTree() {
		loadTreeData();
	}

	public void upload(UploadEvent event) {
		UploadItem item = event.getUploadItem();

		if (item.isTempFile()) {
			File f = item.getFile();
			File dir = new File(getPath() + File.separator);
			boolean success = f.renameTo(new File(dir, item.getFileName()));
			if (!success) {
				this.setMsg("Não foi possível gravar o relatório!");
			} else {
				this.setMsg("Relatório gravado com sucesso!");
			}
		} else {
			this.setMsg("Não foi possível gravar o relatório!");
		}
		this.loadTreeData();

	}

	public TreeState getTreeState() {
		return treeState;
	}

	public void setTreeState(TreeState treeState) {
		this.treeState = treeState;
	}

	public Set<ReportItem> getTree() {
		return tree;
	}

	public void setTree(Set<ReportItem> tree) {
		this.tree = tree;
	}

	public ReportItem getSelectedNode() {
		if (selectedNode == null) {
			return EMPTY_REPORT;
		}
		return selectedNode;
	}

	public void setSelectedNode(ReportItem selectedNode) {
		this.selectedNode = selectedNode;
	}

	public String getFolderName() {
		return folderName;
	}

	public void setFolderName(String folderName) {
		this.folderName = folderName;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public HtmlForm getReportCmp() {
		return reportCmp;
	}

	public void setReportCmp(HtmlForm reportCmp) {
		this.reportCmp = reportCmp;
	}

	/**
	 * Creates a New Folder
	 */
	public void createNewFolder() {
		File newFolder = getFolderPath();
		if (!newFolder.mkdir()) {
			// TODO: Fazer remoção de caracteres especiais?
			this
					.setMsg("Não foi possível criar grupo. Verifique as permissões e remova os caracteres especiais");
		} else {
			loadTreeData();
			this.setMsg("Grupo criado!");
		}
		this.selectRoot();
		this.setFolderName("");

	}

	private File getFolderPath() {
		String path = getPath();

		File newFolder = new File(path + File.separator + this.folderName);
		return newFolder;
	}

	private String getPath() {
		String path = "";
		if (this.selectedNode == null) {
			path = this.path;
		} else {
			path = this.selectedNode.getFullPath();
		}
		return path;
	}

	/**
	 * Deletes a folder
	 */
	public void deleteFolder() {
		File deleteFolder = getFolderPath();

		if (!deleteDir(deleteFolder)) {
			this.setMsg("Não foi possível remover grupo.");
		} else {
			loadTreeData();
			this.setMsg("Grupo removido!");
		}
		this.selectRoot();
	}

	/**
	 * Deletes a folder
	 */
	public void deleteReport() {
		File deleteReport = getFolderPath();

		if (!deleteReport.delete()) {
			this.setMsg("Não foi possível remover relatório.");
		} else {
			String compiledReportPath = deleteReport.getAbsolutePath()
					.replaceFirst(".jrxml", ".jasper");
			new File(compiledReportPath).delete();
			loadTreeData();
			this.setMsg("Relatório removido!");
		}
		this.selectRoot();
	}

	private static boolean deleteDir(File dir) {
		if (dir.isDirectory()) {
			String[] children = dir.list();
			for (int i = 0; i < children.length; i++) {
				boolean success = deleteDir(new File(dir, children[i]));
				if (!success) {
					return false;
				}
			}
		}
		return dir.delete();
	}

	private ReportItem loadSons(File f) {
		ReportItem son = new ReportItem(f.getName(), f.getAbsolutePath(), false);
		if (f.isDirectory()) {
			File[] subs = f.listFiles();
			if (subs.length == 0) {
				ReportItem item = new ReportItem("Nenhum Relatório", "-", false);
				son.getSons().add(item);
			} else {
				for (File sub : subs) {
					if (sub.isDirectory()) {
						son.getSons().add(loadSons(sub));
					} else {
						String name = sub.getName();
						if (name.endsWith(".jrxml")) {
							ReportItem report = new ReportItem(name.substring(
									0, name.lastIndexOf(".jrxml")), sub
									.getAbsolutePath(), true);
							son.getSons().add(report);
						}
					}
				}
			}

		}
		return son;
	}

	public void nodeSelectedListener(NodeSelectedEvent event) {
		ReportItem item = (ReportItem) ((UIData) event.getComponent())
				.getRowData();
		this.setSelectedNode(item);
		if (item.isReport()) {
			reportCmp.getChildren().clear();
			putChildren();
		}
	}

	public void selectRoot() {
		this.setSelectedNode(null);
		getTreeState().setSelected(null);
	}

	private void loadTreeData() {
		this.setMsg("");
		tree = new HashSet<ReportItem>();
		File f = new File(this.path);
		if (f.isDirectory()) {
			File[] subs = f.listFiles();
			for (File sub : subs) {
				tree.add(loadSons(sub));
			}
		} else {
			String name = f.getName();
			if (name.endsWith(".jrxml")) {
				ReportItem report = new ReportItem(name.substring(0, name
						.lastIndexOf(".jrxml")), f.getAbsolutePath(), true);
				tree.add(report);
			}
		}

	}

	private void putChildren() {
		List<UIComponent> components = null;
		components = new ArrayList<UIComponent>();
		File jrxmlFile = new File(this.getSelectedNode().getFullPath());
		boolean includeMaxCount = false;
		boolean includeIgnorePagination = false;
		JSFComponentMapper mapper = new RFComponentMapper();
		Application app = FacesContext.getCurrentInstance().getApplication();

		try {
			JasperDesign jasperDesign = JRXmlLoader.load(jrxmlFile);
			JRParameter[] params = jasperDesign.getParameters();

			int totalParams = 0;

			for (JRParameter p : params) {
				String componentType = mapper
						.getJSFComponent(p.getValueClass());
				if (componentType != null) {
					totalParams++;
					String textLabel = p.getName();
					if (REPORT_MAX_COUNT_TOKEN.equals(p.getName())) {
						if (includeMaxCount) {
							textLabel = "Núm máximo de registros";
						} else {
							continue;
						}
					}
					if (IS_IGNORE_PAGINATION_TOKEN.equals(p.getName())) {
						if (includeIgnorePagination) {
							textLabel = "Separar o relatório em páginas";
						} else {
							continue;
						}

					}

					HtmlOutputText text = (HtmlOutputText) app
							.createComponent(HtmlOutputText.COMPONENT_TYPE);
					String id = p.getName().replaceAll(" ", "__20__");
					text.setId("txt" + id);
					text.setValue(textLabel + ": ");
					components.add(text);
					UIComponent component = app.createComponent(componentType);
					component.setId(id);
					// TODO: Criar classe auxiliar
					if (componentType.equals(HtmlCalendar.COMPONENT_TYPE)) {
						HtmlCalendar cal = ((HtmlCalendar) component);
						cal.setLocale(BRAZIL_LOC);
						cal.setDatePattern("dd/MM/yyyy");
					} else if (componentType
							.equals(HtmlInputNumberSpinner.COMPONENT_TYPE)) {
						HtmlInputNumberSpinner sp = ((HtmlInputNumberSpinner) component);
						sp.setMaxValue(String.valueOf(Integer.MAX_VALUE));
					}
					components.add(component);

				}
			}
			if (totalParams > 0) {
				// Grid
				HtmlPanelGrid panelGrid = (HtmlPanelGrid) app
						.createComponent(HtmlPanelGrid.COMPONENT_TYPE);
				// panelGrid.setId("panelGrid");
				panelGrid.setColumns(2);
				panelGrid.getChildren().addAll(components);
				reportCmp.getChildren().add(panelGrid);


				HtmlSelectOneRadio selectType = (HtmlSelectOneRadio) app
						.createComponent(HtmlSelectOneRadio.COMPONENT_TYPE);
				selectType.setId("REPORT_TYPE");

				ArrayList<SelectItem> radioBtnOptionsList = new ArrayList<SelectItem>();
				radioBtnOptionsList.add(new SelectItem(ReportServlet.PDF_TYPE,
						"pdf"));
				radioBtnOptionsList.add(new SelectItem(ReportServlet.XLS_TYPE,
						"excel"));
//				radioBtnOptionsList.add(new SelectItem(ReportServlet.TXT_TYPE,
//						"txt"));
				radioBtnOptionsList.add(new SelectItem(ReportServlet.HTML_TYPE,
						"html"));

				/** Adding a group of CheckBox Options */
				UISelectItems radioBtnOptions = new UISelectItems();
				radioBtnOptions.setValue(radioBtnOptionsList);

				// Add radioButton options.
				selectType.getChildren().add(radioBtnOptions);

				// Select a default value.
				// This will set the "First Option" to be selected default wise
				selectType.setValue(ReportServlet.PDF_TYPE);
				reportCmp.getChildren().add(selectType);

				// Hidden field
				HtmlInputHidden reportPathHidden = (HtmlInputHidden) app
						.createComponent(HtmlInputHidden.COMPONENT_TYPE);
				reportPathHidden.setId(REPORT_PATH_PARAM);
				reportPathHidden.setValue(this.getSelectedNode().getFullPath());
				reportCmp.getChildren().add(reportPathHidden);

				// Button
				HtmlCommandButton generateReportBtn = (HtmlCommandButton) app
						.createComponent(HtmlCommandButton.COMPONENT_TYPE);
				generateReportBtn.setId(GENERATE_BTN_PARAM);
				generateReportBtn.setValue("Gerar relatório"); // TODO:
				// Externalizar
				// string

				String reportUrl = FacesContext.getCurrentInstance()
						.getExternalContext().getRequestContextPath()
						+ "/report/" + new Date().getTime() + ".report";
				// String onclickAction = "rich:component('" + reportCmp.getId()
				// + "').action='" + reportUrl + "';";
				//
				// System.out.println(reportCmp.getId());

				// String onclickAction = "alert(document.forms[0].action='"
				// + reportUrl + "'); document.forms[0].action='"
				// + reportUrl + "';";
				// generateReportBtn.setOnclick(onclickAction);
				reportCmp.setOnsubmit("this.action='" + reportUrl + "';");

				reportCmp.getChildren().add(generateReportBtn);


			}
		} catch (JRException e1) {
			e1.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
